package com.gitee.wsl.compose.modifier.style

import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.drawscope.withTransform
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.gitee.wsl.compose.modifier.ModifierDrawScope
import com.gitee.wsl.compose.modifier.ModifierStyleScope


/**
 * 绘制虚线
 */
internal fun Modifier.dashedBorder(
    width: Dp,
    color: Color,
    dashLength: Dp = 4.dp,
    gapLength: Dp = 3.dp,
    shape: Shape = RectangleShape,
): Modifier = this.drawBehind {
    val strokeWidthPx = width.toPx()
    val dashPx = dashLength.toPx()
    val gapPx = gapLength.toPx()

    val pathEffect = PathEffect.dashPathEffect(
        intervals = floatArrayOf(dashPx, gapPx),
        phase = 0f
    )

    val inset = strokeWidthPx / 2
    val insetSize = size.copy(
        width = size.width - strokeWidthPx,
        height = size.height - strokeWidthPx
    )

    withTransform(
        transformBlock = {
            translate(left = inset, top = inset)
        }
    ) {
        drawOutline(
            outline = shape.createOutline(insetSize, layoutDirection, this),
            style = Stroke(width = strokeWidthPx, pathEffect = pathEffect),
            color = color
        )
    }
}

@Stable
internal fun Modifier.dashBorder(
    color: Color = Color.Black,
    width: Dp = 1.dp,
    cornerRadiusDp: Dp = 0.dp,
    dashLength: Dp,
) = drawBehind {
    drawRoundRect(
        color = color,
        style = Stroke(
            width = width.toPx(),
            pathEffect = PathEffect.dashPathEffect(
                // 简单起见，让空白和线段的长度相同
                intervals = floatArrayOf(dashLength.toPx(),dashLength.toPx()),
                phase = 0f
            )
        ),
        cornerRadius = CornerRadius(cornerRadiusDp.toPx())
    )

}

@Stable
internal fun Modifier.dashAnimBorder(
    color: Color = Color.Black,
    width: Dp = 1.dp,
    dashLength: Dp,
    cornerRadiusDp: Dp = 0.dp,
) = composed {
    // 不在drawScope 中，无法直接使用 dp.toPx()
    val density = LocalDensity.current
    val dashLengthPx = density.run { dashLength.toPx() }
    // 声明一个无限循环动画
    val infinite = rememberInfiniteTransition(label = "")
    val anim by infinite.animateFloat(initialValue = 0f,
        targetValue = dashLengthPx*2,//偏移一个完整长度
        animationSpec = infiniteRepeatable(
            animation = tween(1000, easing = LinearEasing),
            repeatMode = RepeatMode.Restart // 动画循环模式为 restart
        ), label = ""
    )

    drawBehind {
        drawRoundRect(
            color = color,
            style = Stroke(
                width = width.toPx(),
                pathEffect = PathEffect.dashPathEffect(
                    intervals = floatArrayOf(dashLength.toPx(),dashLength.toPx()),
                    phase = anim // 动画应用
                )
            ),
            cornerRadius = CornerRadius(cornerRadiusDp.toPx())
        )
    }
}

@Stable
internal fun Modifier.dashAnimBorder(
    color: Color = Color.Black,
    width: Dp = 1.dp,
    dashLength: Dp,
    cornerRadiusDp: Dp = 0.dp,
    animate: Boolean = true
) = composed {

    var lastAnimValue by remember { mutableStateOf(0f) }
    val anim = remember(animate) { Animatable(lastAnimValue) }

    val density = LocalDensity.current
    val dashLengthPx = density.run { dashLength.toPx() }

    LaunchedEffect(animate) {
        if (animate) {
            anim.animateTo(
                (dashLengthPx * 2 + lastAnimValue),
                animationSpec =
                infiniteRepeatable(
                    animation = tween(1000, easing = LinearEasing),
                    repeatMode = RepeatMode.Restart,
                )
            ) {
                lastAnimValue = value // store the anim value
            }
        }
    }

    drawBehind {
        drawRoundRect(
            color = color,
            style = Stroke(
                width = width.toPx(),
                pathEffect = PathEffect.dashPathEffect(
                    intervals = floatArrayOf(dashLength.toPx(),dashLength.toPx()),
                    phase = anim.value
                )
            ),
            cornerRadius = CornerRadius(cornerRadiusDp.toPx())
        )
    }
}

val DIRECTIONS = arrayListOf(
    1 to 1, 1 to -1, -1 to 1, -1 to -1
)

@Stable
internal fun Modifier.drawFocusRect(
    focusOffset: Offset? = null,
    color: Color = Color.Yellow,
    rectTotalLength: Int = 100,
    rectVisiblePercent: Float = 0.5f,
    maxScale: Float = 1.3f,
    scaleDurationPercent: Float = 0.3f,
) = composed {
    if (focusOffset == null){
        this
    } else {
        val anim = remember {
            Animatable(0f)
        }
        LaunchedEffect(key1 = focusOffset){
            anim.snapTo(0f)
            anim.animateTo(1f)
        }

        this.then(
            Modifier.drawWithContent {
                drawContent()
                DIRECTIONS.forEach { p ->
                    val fx = ((1-maxScale)/scaleDurationPercent*(anim.value-1)+maxScale)
                    val x = focusOffset.x + p.first * rectTotalLength / 2 * fx
                    val y = focusOffset.y + p.second * rectTotalLength / 2 * fx

                    val x2 = x-rectVisiblePercent/2*rectTotalLength*p.first
                    val y2 = y-rectVisiblePercent/2*rectTotalLength*p.second
                    drawLine(color, Offset(x, y), Offset(x, y2), strokeWidth = 8f)
                    drawLine(color, Offset(x, y), Offset(x2, y), strokeWidth = 8f)
                }
            }
        )
    }
}

fun ModifierStyleScope.dashAnimBorder(
    color: Color = Color.Black,
    width: Dp = 1.dp,
    dashLength: Dp,
    cornerRadiusDp: Dp = 0.dp,
    animate: Boolean = true
){
    modifier = modifier.dashAnimBorder(color, width, dashLength, cornerRadiusDp, animate)
}

fun ModifierStyleScope.dashAnimBorder(
    color: Color = Color.Black,
    width: Dp = 1.dp,
    dashLength: Dp,
    cornerRadiusDp: Dp = 0.dp,
){
    modifier = modifier.dashAnimBorder(color, width, dashLength, cornerRadiusDp)
}

fun ModifierStyleScope.dashBorder(
    color: Color = Color.Black,
    width: Dp = 1.dp,
    cornerRadiusDp: Dp = 0.dp,
    dashLength: Dp,
){
    modifier = modifier.dashBorder(color, width, cornerRadiusDp, dashLength)
}

fun ModifierDrawScope.drawFocusRect(
    focusOffset: Offset? = null,
    color: Color = Color.Yellow,
    rectTotalLength: Int = 100,
    rectVisiblePercent: Float = 0.5f,
    maxScale: Float = 1.3f,
    scaleDurationPercent: Float = 0.3f,
){
    modifier = modifier.drawFocusRect(focusOffset, color, rectTotalLength, rectVisiblePercent, maxScale, scaleDurationPercent)
}